package org.dru.clay.util;

import java.io.File;
import java.util.ArrayList;
import java.util.StringTokenizer;
import java.util.regex.Pattern;

import org.dru.clay.util.functional.Predicate;

public class FileMatcherPredicate implements Predicate<String> {
	private static final Pattern singleStar = Pattern.compile("(?<!\\*)\\*(?!\\*)", Pattern.DOTALL);
	private static final Pattern quote = Pattern.compile("\\.", Pattern.DOTALL);
	private static final Pattern question = Pattern.compile("\\?", Pattern.DOTALL);

	private final Pattern[] part;

	public FileMatcherPredicate(String original) {
		ArrayList<Pattern> matchList = new ArrayList<Pattern>();

		boolean previousNull = false;
		final StringTokenizer tokenizer = new StringTokenizer(original, File.separator);
		while (tokenizer.hasMoreTokens()) {
			String token = tokenizer.nextToken();
			if ("**".equals(token)) {
				if (!previousNull) {
					matchList.add(null);
				}
				previousNull = true;
				continue;
			}
			previousNull = false;

			token = replace(token, quote, "\\\\.");
			token = replace(token, question, "\\.?");
			token = replace(token, singleStar, ".*");
			matchList.add(Pattern.compile(token));
		}

		if (original.endsWith(File.separator)) {
			matchList.add(null);
		}

		part = matchList.toArray(new Pattern[] {});
	}

	@Override
	public boolean apply(String value) {
		if (value == null) {
			return false;
		}
		
		int index = 0;
		final StringTokenizer tokenizer = new StringTokenizer(value, File.separator);
		while (tokenizer.hasMoreTokens() && index < part.length) {
			String token = tokenizer.nextToken();
			if (part[index] == null) {
				if (index+1 >= part.length) {
					return true;
				}				
				if (part[index+1].matcher(token).matches()) {
					index += 2;
				}
			} else {
				if (!part[index].matcher(token).matches()) {
					return false;
				}				
				index++;
			}
		}
		
		if (tokenizer.hasMoreTokens()) {
			return false;
		}
		
		if (index+1 == part.length) {
			return part[index] == null;
		}
		
		return index == part.length;
	}

	private String replace(String source, Pattern pattern, String replace) {
		return pattern.matcher(source).replaceAll(replace);
	}
}
